classdef SerialApp2 < matlab.apps.AppBase

    % Properties that correspond to app components
    properties (Access = public)
        UIFigure              matlab.ui.Figure
        SerialParityBits      matlab.ui.control.DropDown
        Label_7               matlab.ui.control.Label
        SerialStopBits        matlab.ui.control.DropDown
        Label_6               matlab.ui.control.Label
        SerialDataBits        matlab.ui.control.DropDown
        Label_5               matlab.ui.control.Label
        SerialBaud            matlab.ui.control.DropDown
        Label_4               matlab.ui.control.Label
        Image                 matlab.ui.control.Image
        Switch                matlab.ui.control.Switch
        Switch3Label          matlab.ui.control.Label
        Button_send_clear     matlab.ui.control.Button
        Button_send           matlab.ui.control.Button
        Label_2               matlab.ui.control.Label
        Label                 matlab.ui.control.Label
        Button_save           matlab.ui.control.Button
        Button                matlab.ui.control.Button
        CheckBox_hex_display  matlab.ui.control.CheckBox
        Text_send             matlab.ui.control.TextArea
        TextArea              matlab.ui.control.TextArea
        Lamp                  matlab.ui.control.Lamp
        Button_open_serial    matlab.ui.control.StateButton
        Button_search         matlab.ui.control.Button
        SerialPortNumber      matlab.ui.control.DropDown
        Label_3               matlab.ui.control.Label
        EncodingDropDown      matlab.ui.control.DropDown
        Label_8               matlab.ui.control.Label
    end

    
    properties (Access = private)
        flag = 1; % 标志位，用于判断是否是首次接收数据
        SerialObject; % 串口对象，用于与串口进行通信
        Str_Data; % 可能用于存储接收到的数据字符串，此处未详细使用
    end
    
    methods (Access = private)
        
        % 串口配置函数，用于打开和关闭串口
        function serialconfig(app)
            % 获取所有可用串口列表，并将其设置为串口选择下拉框的选项
            app.SerialPortNumber.Items = serialportlist("all");
            % 检查打开串口按钮的状态，为 1 表示要打开串口
            if app.Button_open_serial.Value
                % 如果之前已经创建了串口对象，先删除该对象（安全检查）
                if ~isempty(app.SerialObject) && isvalid(app.SerialObject)
                    try delete(app.SerialObject); catch, end
                end
                % 将指示灯颜色设置为绿色，表示串口处于打开状态
                app.Lamp.Color = 'g';
                % 修改打开串口按钮的文本为"关闭串口"
                app.Button_open_serial.Text = '关闭串口';
                try
                    % 尝试创建一个新的串口对象
                    app.SerialObject = serialport(app.SerialPortNumber.Value, ...
                        str2double(app.SerialBaud.Value), ...
                        "DataBits", str2double(app.SerialDataBits.Value), ...
                        "StopBits", str2double(app.SerialStopBits.Value), ...
                        "Parity", app.SerialParityBits.Value);
                    
                    % 配置串口对象的终止符为"CR/LF"（回车换行）
                    configureTerminator(app.SerialObject, "CR/LF");
                    
                    % 注意：serialport对象没有Encoding属性，编码处理在回调函数中进行
                    
                    % 使用字节回调而不是终止符回调，避免readline超时问题
                    try
                        configureCallback(app.SerialObject, "byte", 1, @(src, event)serialcallback(app, src, event));
                        fprintf('配置了字节回调\n');
                    catch ME
                        fprintf('字节回调配置失败: %s\n', ME.message);
                        % 备用方案：使用终止符回调
                        try
                            configureCallback(app.SerialObject, "terminator", @(src, event)serialcallback(app, src, event));
                            fprintf('配置了终止符回调作为备用方案\n');
                        catch ME2
                            fprintf('终止符回调也配置失败: %s\n', ME2.message);
                        end
                    end
                    
                    % 设置串口缓冲区大小
                    app.SerialObject.InputBufferSize = 1024;
                    app.SerialObject.OutputBufferSize = 1024;
                    
                    % 设置超时时间
                    app.SerialObject.Timeout = 1;
                    
                    % 清除输入缓冲区
                    flush(app.SerialObject);
                    
                    fprintf('串口配置完成，等待数据...\n');
                    
                                         % 发送测试数据以验证连接
                     try
                         testData = 'SerialApp2 测试消息 - 中文测试';
                         % 将字符串转换为字节数组发送
                         testBytes = uint8(testData);
                         write(app.SerialObject, testBytes, 'uint8');
                         fprintf('已发送测试数据: %s (字节: %s)\n', testData, mat2str(testBytes));
                         fprintf('注意：默认编码已设置为GB2312，适用于接收GB2312编码的数据\n');
                     catch ME
                         fprintf('测试数据发送失败: %s\n', ME.message);
                     end
                catch ME
                    % 如果创建串口对象过程中出现异常，弹出消息框提示串口打开失败
                    msgbox(['串口打开失败: ' ME.message]);
                    % 将指示灯颜色设置为红色，表示串口打开失败
                    app.Lamp.Color = 'r';
                    % 修改打开串口按钮的文本为"打开串口"
                    app.Button_open_serial.Text = '打开串口';
                    % 删除之前创建的串口对象（安全检查）
                    if ~isempty(app.SerialObject) && isvalid(app.SerialObject)
                        try delete(app.SerialObject); catch, end
                    end
                end
            else
                % 如果打开串口按钮未按下，即要关闭串口
                % 删除之前创建的串口对象（安全检查）
                if ~isempty(app.SerialObject) && isvalid(app.SerialObject)
                    try delete(app.SerialObject); catch, end
                end
                % 将指示灯颜色设置为红色，表示串口处于关闭状态
                app.Lamp.Color = 'r';
                % 修改打开串口按钮的文本为"打开串口"
                app.Button_open_serial.Text = '打开串口';
            end
        end

        % 串口数据接收回调函数，当串口接收到数据时调用
        function serialcallback(app, src, ~)
            % 添加调试信息
            fprintf('串口回调函数被触发\n');
            
            % 根据复选框的状态决定数据显示方式
            if app.CheckBox_hex_display.Value == 0
                try
                    % 优先使用read方法读取数据，避免readline超时问题
                    data = '';
                                         if src.NumBytesAvailable > 0
                         try
                             % 方法1：直接读取可用字节
                             data = read(src, src.NumBytesAvailable, 'uint8');
                             fprintf('使用read读取到字节数据: %s\n', mat2str(data));
                             % 将字节数组转换为字符串
                             data = char(data);
                         catch
                             % 方法2：如果read失败，尝试readline
                             try
                                 data = readline(src);
                                 fprintf('使用readline读取到数据: %s\n', data);
                             catch ME
                                 fprintf('读取数据失败: %s\n', ME.message);
                                 return;
                             end
                         end
                     else
                         fprintf('没有可用数据\n');
                         return;
                     end
                    
                    % 检查数据是否为空
                    if isempty(data)
                        fprintf('读取到空数据，跳过处理\n');
                        return;
                    end
                    
                    % 确保data是字符串类型
                    if ~ischar(data) && ~isstring(data)
                        data = char(data);
                    end
                    
                    % 去除读取数据首尾的空白字符（如回车符、换行符等）
                    cleanData = strtrim(data);
                    
                                         % 尝试修复编码问题
                     if ~isempty(cleanData)
                         % 将数据转换为字节数组进行处理
                         dataBytes = uint8(cleanData);
                         
                         % 根据用户选择的编码进行解码
                         selectedEncoding = app.EncodingDropDown.Value;
                         
                         try
                             switch selectedEncoding
                                 case 'UTF-8'
                                     cleanData = native2unicode(dataBytes, 'UTF-8');
                                 case 'GBK'
                                     cleanData = native2unicode(dataBytes, 'GBK');
                                 case 'GB2312'
                                     cleanData = native2unicode(dataBytes, 'GB2312');
                                 case 'ASCII'
                                     cleanData = char(dataBytes);
                                 case 'Auto'
                                     % 自动检测编码
                                     try
                                         cleanData = native2unicode(dataBytes, 'UTF-8');
                                     catch
                                         try
                                             cleanData = native2unicode(dataBytes, 'GBK');
                                         catch
                                             try
                                                 cleanData = native2unicode(dataBytes, 'GB2312');
                                             catch
                                                 cleanData = char(dataBytes);
                                             end
                                         end
                                     end
                             end
                             fprintf('编码处理完成: %s -> %s\n', mat2str(dataBytes), cleanData);
                         catch ME
                             % 如果解码失败，显示原始十六进制数据
                             fprintf('编码处理失败: %s\n', ME.message);
                             cleanData = sprintf('[编码错误] %s', mat2str(dataBytes));
                         end
                     else
                         % 如果cleanData为空，直接使用原始数据
                         cleanData = data;
                     end
                     
                     % 确保cleanData是字符串类型
                     if ~ischar(cleanData) && ~isstring(cleanData)
                         cleanData = char(cleanData);
                     end
                    
                    % 如果是首次接收数据，覆盖接收文本框原有的内容
                    if app.flag == 1
                        app.TextArea.Value = cleanData;
                        % 将标志位设置为 0，后续接收的数据将追加到文本区域
                        app.flag = 0;
                    else
                        % 检查去除空白后的读取数据是否为空
                        if ~isempty(cleanData)
                            % 将读取的数据追加到接收文本框中
                            app.TextArea.Value = [app.TextArea.Value; cleanData];
                        end
                    end
                    % 将文本区域滚动到最新数据的位置，方便查看最新接收的数据
                    app.TextArea.scroll('bottom');
                catch ME
                    % 处理可能出现的异常，将异常信息打印到命令窗口
                    fprintf('Error in serialcallback: %s\n', ME.message);
                end
            elseif app.CheckBox_hex_display.Value == 1
                % 优先使用read方法读取数据，避免readline超时问题
                data = '';
                if src.NumBytesAvailable > 0
                    try
                        % 方法1：直接读取可用字节
                        data = read(src, src.NumBytesAvailable, 'uint8');
                        fprintf('十六进制模式使用read读取到字节数据: %s\n', mat2str(data));
                        % 将字节数组转换为字符串
                        data = char(data);
                    catch
                        % 方法2：如果read失败，尝试readline
                        try
                            data = readline(src);
                            fprintf('十六进制模式使用readline读取到数据: %s\n', data);
                        catch ME
                            fprintf('十六进制模式读取数据失败: %s\n', ME.message);
                            return;
                        end
                    end
                else
                    fprintf('十六进制模式没有可用数据\n');
                    return;
                end
                
                % 检查数据是否为空
                if isempty(data)
                    fprintf('十六进制模式读取到空数据，跳过处理\n');
                    return;
                end
                
                % 确保data是字符串类型
                if ~ischar(data) && ~isstring(data)
                    data = char(data);
                end
                
                % 去除读取数据首尾的空白字符（如回车符、换行符等）
                cleanData = strtrim(data);
                
                                 % 尝试修复编码问题并处理十六进制显示
                 if ~isempty(cleanData)
                     % 将数据转换为字节数组进行处理
                     dataBytes = uint8(cleanData);
                     
                     % 根据用户选择的编码进行解码
                     selectedEncoding = app.EncodingDropDown.Value;
                     
                     try
                                                   switch selectedEncoding
                              case 'UTF-8'
                                  decodedText = native2unicode(dataBytes, 'UTF-8');
                              case 'GBK'
                                  decodedText = native2unicode(dataBytes, 'GBK');
                              case 'GB2312'
                                  decodedText = native2unicode(dataBytes, 'GB2312');
                              case 'ASCII'
                                  decodedText = char(dataBytes);
                              case 'Auto'
                                  % 自动检测编码
                                  try
                                      decodedText = native2unicode(dataBytes, 'UTF-8');
                                  catch
                                      try
                                          decodedText = native2unicode(dataBytes, 'GBK');
                                      catch
                                          try
                                              decodedText = native2unicode(dataBytes, 'GB2312');
                                          catch
                                              decodedText = char(dataBytes);
                                          end
                                      end
                                  end
                          end
                         fprintf('十六进制模式编码处理完成: %s -> %s\n', mat2str(dataBytes), decodedText);
                     catch ME
                         % 如果解码失败，使用原始字节数据
                         fprintf('十六进制模式编码处理失败: %s\n', ME.message);
                         decodedText = char(dataBytes);
                     end
                    
                    % 如果是首次接收数据，覆盖接收文本框原有的内容
                    if app.flag == 1
                        % 构建十六进制显示字符串
                        hexArray = dec2hex(dataBytes, 2);
                        hexStr = strjoin(cellstr(hexArray), ' ');
                        
                        % 检查是否包含可打印字符
                        if all(dataBytes >= 32 & dataBytes <= 126)
                            displayText = sprintf('%s [HEX: %s]', decodedText, hexStr);
                        else
                            displayText = sprintf('[BINARY] HEX: %s', hexStr);
                        end
                        
                        app.TextArea.Value = displayText;
                        % 将标志位设置为 0，后续接收的数据将追加到文本区域
                        app.flag = 0;
                    else
                        % 使用换行符（'\r' 和 '\n'）分割多行数据
                        newLines = strsplit(cleanData, {'\r', '\n'}, 'CollapseDelimiters', true);
                        % 移除分割后为空的单元格
                        newLines = newLines(~cellfun(@isempty, newLines));
                        % 初始化一个与 newLines 大小相同的单元格数组，用于存储处理后的数据
                        processedLines = cell(size(newLines));
                        
                        % 遍历分割后的每一行数据
                        for i = 1:length(newLines)
                            % 获取当前行的原始字符串
                            rawStr = newLines{i};
                            rawBytes = uint8(rawStr);
                            
                            % 根据用户选择的编码解码当前行
                                                         try
                                 switch selectedEncoding
                                     case 'UTF-8'
                                         decodedLine = native2unicode(rawBytes, 'UTF-8');
                                     case 'GBK'
                                         decodedLine = native2unicode(rawBytes, 'GBK');
                                     case 'GB2312'
                                         decodedLine = native2unicode(rawBytes, 'GB2312');
                                     case 'ASCII'
                                         decodedLine = char(rawBytes);
                                     case 'Auto'
                                         % 自动检测编码
                                         try
                                             decodedLine = native2unicode(rawBytes, 'UTF-8');
                                         catch
                                             try
                                                 decodedLine = native2unicode(rawBytes, 'GBK');
                                             catch
                                                 try
                                                     decodedLine = native2unicode(rawBytes, 'GB2312');
                                                 catch
                                                     decodedLine = char(rawBytes);
                                                 end
                                             end
                                         end
                                 end
                            catch
                                decodedLine = char(rawBytes);
                            end
                            
                            % 将原始字符串转换为十六进制表示，每个字节用两位十六进制数表示
                            hexArray = dec2hex(rawBytes, 2);
                            % 将十六进制数组转换为以空格分隔的字符串
                            hexStr = strjoin(cellstr(hexArray), ' ');
                            
                            % 检查原始字符串中的字符是否都为可打印的 ASCII 字符（ASCII 码范围 32 到 126）
                            if all(rawBytes >= 32 & rawBytes <= 126)
                                % 如果是可打印的 ASCII 字符，构建包含原始字符串和十六进制表示的显示字符串
                                processedLines{i} = sprintf('%s [HEX: %s]', decodedLine, hexStr);
                            else
                                % 如果包含非可打印的 ASCII 字符，构建仅包含十六进制表示的显示字符串
                                processedLines{i} = sprintf('[BINARY] HEX: %s', hexStr);
                            end
                        end
                        % 将处理后的数据追加到接收文本框中
                        app.TextArea.Value = [app.TextArea.Value; processedLines'];
                    end
                    
                    % 设置文本区域最多保留的行数
                    maxLines = 200;
                    % 如果文本区域的行数超过最大行数，只保留最后 maxLines 行数据
                    if numel(app.TextArea.Value) > maxLines
                        app.TextArea.Value = app.TextArea.Value(end - maxLines + 1:end);
                    end
                    % 将文本区域滚动到最新数据的位置，方便查看最新接收的数据
                    app.TextArea.scroll('bottom');
                end
                % 强制刷新界面，确保界面及时更新显示最新的数据
                drawnow limitrate;
            end
        end
    end
    

    % Callbacks that handle component events
    methods (Access = private)

        % Code that executes after component creation
        function startupFcn(app)
             % 获取所有可用串口列表，并将其设置为串口选择下拉框的选项
            app.SerialPortNumber.Items = serialportlist("all");
            % 将指示灯颜色设置为红色，表示串口处于关闭状态
            app.Lamp.Color = 'r';
            % 设置打开串口按钮的文本为"打开串口"
            app.Button_open_serial.Text = "打开串口";
            % 清空接收文本框
            app.TextArea.Value = '';
            % 清空发送文本框
            app.Text_send.Value = '';
        end

        % Button pushed function: Button_search
        function Button_searchPushed(app, event)
            % 获取所有可用串口列表，并将其设置为串口选择下拉框的选项
            app.SerialPortNumber.Items = serialportlist("all");
        end

        % Value changed function: Switch
        function SwitchValueChanged(app, event)
            % 获取开关组件的当前值
            value = app.Switch.Value;
            % 根据开关的值设置指示灯的颜色
            switch lower(value)
                case "off"
                    app.Lamp.Color = 'r';
                case "on"
                    app.Lamp.Color = 'g';
            end
        end

        % Value changed function: SerialPortNumber
        function SerialPortNumberValueChanged(app, event)
            value = app.SerialPortNumber.Value;
            fprintf(value);
            
        end

        % Button pushed function: Button
        function ButtonPushed(app, event)
             % 清空接收文本框
            app.TextArea.Value = '';
            % 将标志位设置为 1，表示下一次接收数据时覆盖原有内容
            app.flag = 1;
        end

        % Button pushed function: Button_send_clear
        function Button_send_clearPushed(app, event)
             % 清空接收文本框
            app.Text_send.Value = '';
        end

        % Drop down opening function: SerialPortNumber
        function SerialPortNumberDropDownOpening(app, event)
            % 调用串口配置函数
            serialconfig(app);
        end

        % Drop down opening function: SerialBaud
        function SerialBaudDropDownOpening(app, event)
            % 调用串口配置函数
            serialconfig(app);
        end

        % Drop down opening function: SerialDataBits
        function SerialDataBitsDropDownOpening(app, event)
            % 调用串口配置函数
            serialconfig(app);
        end

        % Drop down opening function: SerialStopBits
        function SerialStopBitsDropDownOpening(app, event)
            % 调用串口配置函数
            serialconfig(app);
        end

        % Drop down opening function: SerialParityBits
        function SerialParityBitsDropDownOpening(app, event)
            % 调用串口配置函数
            serialconfig(app);
        end

        % Button pushed function: Button_send
        function Button_sendPushed(app, event)
             % 获取发送文本框中的数据
            textCells = app.Text_send.Value;
            % 获取数据的行数
            numLines = numel(textCells);
            % 初始化要发送的数据
            data = [];
            % 拼接数据并添加换行符
            for i = 1:numLines
                line = textCells{i};
                data = [data, uint8(line)];
                % 前 n-1 行添加 CRLF 换行符
                if i < numLines
                    data = [data, 13, 10];
                end
            end
            % 检查串口状态（更稳健的检查）
            if isempty(app.SerialObject) || ~isvalid(app.SerialObject)
                % 如果串口未连接或已关闭，弹出错误对话框
                errordlg('串口未连接或已关闭！', '错误');
                return;
            end
            % 发送数据
            if ~isempty(data)
                try
                    write(app.SerialObject, data, 'uint8');
                catch ME
                    % 如果发送过程中出现异常，弹出错误对话框
                    errordlg(['发送失败: ' ME.message], '错误');
                end
            end
        end

        % Button pushed function: Button_save
        function Button_savePushed(app, event)
            % 获取接收文本框中的数据，统一转换为 cell array of char
            text = app.TextArea.Value;
            lines = cellstr(text);
            arr = numel(lines);
            % 根据十六进制显示复选框的状态选择保存文件的类型
            if app.CheckBox_hex_display.Value == 0
                % 保存为普通文本文件
                fid = fopen('data.txt', 'w+');
                if fid == -1
                    errordlg('无法创建 data.txt', '文件错误');
                    return;
                end
                for i = 1:arr
                    fprintf(fid, '%s\r\n', lines{i}); % 换行
                end
                fclose(fid);
            elseif app.CheckBox_hex_display.Value == 1
                % 保存为十六进制文本文件
                fid = fopen('data_hex.txt', 'w+');
                if fid == -1
                    errordlg('无法创建 data_hex.txt', '文件错误');
                    return;
                end
                for i = 1:arr
                    fprintf(fid, '%s\r\n', lines{i}); % 换行
                end
                fclose(fid);
            end
        end

        % Value changed function: Button_open_serial
        function Button_open_serialValueChanged(app, event)
            serialconfig(app);
        end
    end

    % Component initialization
    methods (Access = private)

        % Create UIFigure and components
        function createComponents(app)

            % Get the file path for locating images
            pathToMLAPP = fileparts(mfilename('fullpath'));

            % Create UIFigure and hide until all components are created
            app.UIFigure = uifigure('Visible', 'off');
            app.UIFigure.Position = [100 100 640 480];
            app.UIFigure.Name = 'MATLAB App';

            % Create Label_3
            app.Label_3 = uilabel(app.UIFigure);
            app.Label_3.HorizontalAlignment = 'right';
            app.Label_3.Position = [83 433 53 22];
            app.Label_3.Text = '串口选择';

            % Create SerialPortNumber
            app.SerialPortNumber = uidropdown(app.UIFigure);
            app.SerialPortNumber.Items = {'COM1', 'COM2'};
            app.SerialPortNumber.DropDownOpeningFcn = createCallbackFcn(app, @SerialPortNumberDropDownOpening, true);
            app.SerialPortNumber.ValueChangedFcn = createCallbackFcn(app, @SerialPortNumberValueChanged, true);
            app.SerialPortNumber.Position = [151 433 100 22];
            app.SerialPortNumber.Value = 'COM1';

            % Create Button_search
            app.Button_search = uibutton(app.UIFigure, 'push');
            app.Button_search.ButtonPushedFcn = createCallbackFcn(app, @Button_searchPushed, true);
            app.Button_search.Position = [279 432 100 23];
            app.Button_search.Text = '搜索串口';

            % Create Button_open_serial
            app.Button_open_serial = uibutton(app.UIFigure, 'state');
            app.Button_open_serial.ValueChangedFcn = createCallbackFcn(app, @Button_open_serialValueChanged, true);
            app.Button_open_serial.Text = '打开串口';
            app.Button_open_serial.Position = [408 431 100 23];

            % Create Lamp
            app.Lamp = uilamp(app.UIFigure);
            app.Lamp.Position = [569 433 20 20];

            % Create TextArea
            app.TextArea = uitextarea(app.UIFigure);
            app.TextArea.Position = [28 173 292 202];

            % Create Text_send
            app.Text_send = uitextarea(app.UIFigure);
            app.Text_send.Position = [328 173 292 202];

            % Create CheckBox_hex_display
            app.CheckBox_hex_display = uicheckbox(app.UIFigure);
            app.CheckBox_hex_display.Text = '16进制显示';
            app.CheckBox_hex_display.Position = [121 390 83 22];

            % Create Button
            app.Button = uibutton(app.UIFigure, 'push');
            app.Button.ButtonPushedFcn = createCallbackFcn(app, @ButtonPushed, true);
            app.Button.Position = [209 390 42 23];
            app.Button.Text = '清空';

            % Create Button_save
            app.Button_save = uibutton(app.UIFigure, 'push');
            app.Button_save.ButtonPushedFcn = createCallbackFcn(app, @Button_savePushed, true);
            app.Button_save.Position = [278 390 42 23];
            app.Button_save.Text = '保存';

            % Create Label
            app.Label = uilabel(app.UIFigure);
            app.Label.Position = [38 390 65 22];
            app.Label.Text = '接收文本框';

            % Create Label_2
            app.Label_2 = uilabel(app.UIFigure);
            app.Label_2.Position = [350 390 65 22];
            app.Label_2.Text = '发送文本框';

            % Create Button_send
            app.Button_send = uibutton(app.UIFigure, 'push');
            app.Button_send.ButtonPushedFcn = createCallbackFcn(app, @Button_sendPushed, true);
            app.Button_send.Position = [424 390 100 23];
            app.Button_send.Text = '发送数据';

            % Create Button_send_clear
            app.Button_send_clear = uibutton(app.UIFigure, 'push');
            app.Button_send_clear.ButtonPushedFcn = createCallbackFcn(app, @Button_send_clearPushed, true);
            app.Button_send_clear.Position = [553 390 42 23];
            app.Button_send_clear.Text = '清空';

            % Create Switch3Label
            app.Switch3Label = uilabel(app.UIFigure);
            app.Switch3Label.HorizontalAlignment = 'center';
            app.Switch3Label.Position = [484 51 47 22];
            app.Switch3Label.Text = 'Switch3';

            % Create Switch
            app.Switch = uiswitch(app.UIFigure, 'slider');
            app.Switch.Orientation = 'vertical';
            app.Switch.ValueChangedFcn = createCallbackFcn(app, @SwitchValueChanged, true);
            app.Switch.Position = [485 88 20 45];

            % Create Image
            app.Image = uiimage(app.UIFigure);
            app.Image.Position = [541 54 100 100];
            app.Image.ImageSource = fullfile(pathToMLAPP, 'ab.png');

            % Create Label_4
            app.Label_4 = uilabel(app.UIFigure);
            app.Label_4.HorizontalAlignment = 'right';
            app.Label_4.Position = [82 143 41 22];
            app.Label_4.Text = '波特率';

            % Create SerialBaud
            app.SerialBaud = uidropdown(app.UIFigure);
            app.SerialBaud.Items = {'115200', '9600'};
            app.SerialBaud.DropDownOpeningFcn = createCallbackFcn(app, @SerialBaudDropDownOpening, true);
            app.SerialBaud.Position = [138 143 100 22];
            app.SerialBaud.Value = '115200';

            % Create Label_5
            app.Label_5 = uilabel(app.UIFigure);
            app.Label_5.HorizontalAlignment = 'right';
            app.Label_5.Position = [83 111 41 22];
            app.Label_5.Text = '数据位';

            % Create SerialDataBits
            app.SerialDataBits = uidropdown(app.UIFigure);
            app.SerialDataBits.Items = {'8', '10', '12'};
            app.SerialDataBits.DropDownOpeningFcn = createCallbackFcn(app, @SerialDataBitsDropDownOpening, true);
            app.SerialDataBits.Position = [139 111 100 22];
            app.SerialDataBits.Value = '8';

            % Create Label_6
            app.Label_6 = uilabel(app.UIFigure);
            app.Label_6.HorizontalAlignment = 'right';
            app.Label_6.Position = [84 82 41 22];
            app.Label_6.Text = '停止位';

            % Create SerialStopBits
            app.SerialStopBits = uidropdown(app.UIFigure);
            app.SerialStopBits.Items = {'1', '2', '3'};
            app.SerialStopBits.DropDownOpeningFcn = createCallbackFcn(app, @SerialStopBitsDropDownOpening, true);
            app.SerialStopBits.Position = [140 82 100 22];
            app.SerialStopBits.Value = '1';

            % Create Label_7
            app.Label_7 = uilabel(app.UIFigure);
            app.Label_7.HorizontalAlignment = 'right';
            app.Label_7.Position = [86 54 41 22];
            app.Label_7.Text = '校验位';

            % Create SerialParityBits
            app.SerialParityBits = uidropdown(app.UIFigure);
            app.SerialParityBits.Items = {'none', '1', '2'};
            app.SerialParityBits.DropDownOpeningFcn = createCallbackFcn(app, @SerialParityBitsDropDownOpening, true);
            app.SerialParityBits.Position = [142 54 100 22];
            app.SerialParityBits.Value = 'none';

            % Create Label_8
            app.Label_8 = uilabel(app.UIFigure);
            app.Label_8.HorizontalAlignment = 'right';
            app.Label_8.Position = [86 26 41 22];
            app.Label_8.Text = '编码';

            % Create EncodingDropDown
            app.EncodingDropDown = uidropdown(app.UIFigure);
            app.EncodingDropDown.Items = {'UTF-8', 'GBK', 'GB2312', 'ASCII', 'Auto'};
            app.EncodingDropDown.Position = [142 26 100 22];
            app.EncodingDropDown.Value = 'GB2312';

            % Show the figure after all components are created
            app.UIFigure.Visible = 'on';
        end
    end

    % App creation and deletion
    methods (Access = public)

        % Construct app
        function app = SerialApp2

            % Create UIFigure and components
            createComponents(app)

            % Register the app with App Designer
            registerApp(app, app.UIFigure)

            % Execute the startup function
            runStartupFcn(app, @startupFcn)

            if nargout == 0
                clear app
            end
        end

        % Code that executes before app deletion
        function delete(app)

            % Delete UIFigure when app is deleted
            delete(app.UIFigure)
        end
    end
end